__license__ = "MIT License <http://www.opensource.org/licenses/mit-license.php>"
__copyright__ = "2006 Tiago Cogumbreiro"
__author__ = "Tiago Cogumbreiro <cogumbreiro@users.sf.net>"

class _DestroyedListener:
    """
    This is a helper class that helps you not create a circular reference
    with the object you're listening too.
    """
    def __init__(self, holder):
        self.holder = weakref.ref(holder)
    
    def on_destroyed(self, gobj):
        # Remove the old reference
        self.holder().obj = lambda: None

class SignalHolder:
    """
    Simple usage::
    
        holder = SignalHolder(btn, "clicked", on_clicked)
        # while this reference lives the connection to the signal lives too
        holder = None
        # on_connect no longer recieves callbacks from 'btn'
        
        # You can also send user data, you need to use the 'userdata' keyword
        # argument
        holder = SignalHolder(btn, "clicked", on_clicked, userdata="bar")
        
    """
    def __init__(self, obj, signal, cb, use_weakref=True, destroy=True, *args, **kwargs):
        try:
            args = kwargs["userdata"],
        except KeyError:
            args = ()

        self.source = obj.connect(signal, cb, *args)

        if destroy and isinstance(obj, gtk.Object):
            self.listener = _DestroyedListener(self)
            self.destroy_source = obj.connect("destroy", self.listener.on_destroyed)

        if use_weakref:
            self.obj = weakref.ref(obj)
        else:
            self.obj = lambda: obj
    
    def __del__(self):
        obj = self.obj()
        if obj is None:
            return
            
        obj.disconnect(self.source)
        if hasattr(self, "destroy_source"):
            obj.disconnect(self.destroy_source)


################################################################################
import unittest
class TestSignalHolder(unittest.TestCase):
    entry = ""
    
    def on_changed(self, entry):
        self.text = entry.get_text()
        
    def test_simple_ref(self):
        
        entry = gtk.Entry()
        holder = SignalHolder(entry, "changed", self.on_changed)
        assert hasattr(holder, "destroy_source")
        entry.set_text("foo")
        self.assertEquals("foo", self.text)
        
        
        holder = None
        entry.set_text("bar")
        self.assertEquals("foo", self.text)

        holder = SignalHolder(entry, "changed", self.on_changed)
        entry.destroy()
        # holder.obj is a function that returns the object associated
        # with the holder
        assert holder.obj() is None
        entry.set_text("foo2")
        self.assertEquals("foo", self.text)

if __name__ == '__main__':
    unittest.main()
    
